package com.tian.service.impl;

import com.alibaba.fastjson.JSON;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.tian.common.CommonResult;
import com.tian.dto.BaseRespPageDto;
import com.tian.dto.ChargeUserLoginResDto;
import com.tian.dto.GenerateCodeReqDto;
import com.tian.dto.exchange.*;
import com.tian.entity.ChargeCoupon;
import com.tian.entity.ExchangeCode;
import com.tian.enums.ChargeCouponConditionExpireUnitEnum;
import com.tian.enums.CouponStatusEnum;
import com.tian.enums.ExchangeCodeStatusEnum;
import com.tian.enums.ResultCode;
import com.tian.exception.BusinessException;
import com.tian.mapper.ChargeCouponMapper;
import com.tian.mapper.ExchangeCodeMapper;
import com.tian.service.ChargeCouponService;
import com.tian.service.ExchangeCodeService;
import com.tian.strategy.CalculateDiscountAmountService;
import com.tian.strategy.CalculateDiscountAmountStrategy;
import com.tian.util.*;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RBucket;
import org.redisson.api.RedissonClient;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.sql.Timestamp;
import java.time.LocalDate;
import java.time.LocalTime;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Objects;

/**
 * {@code @description:} 兑换码服务实现类
 *
 * @author tianwc 公众号：Java后端技术全栈
 * 在线刷题 1200+java面试题和1000+篇技术文章：<a href="https://woaijava.cc/">博客地址</a>
 * {@code @date:} 2024-11-13 9:46
 * {@code @version:} 1.0
 */
@Slf4j
@Service
public class ExchangeCodeServiceImpl implements ExchangeCodeService {
    @Resource
    private ExchangeCodeMapper exchangeCodeMapper;
    @Resource
    private ChargeCouponMapper chargeCouponMapper;
    @Resource
    private ChargeCouponService chargeCouponService;
    @Resource
    private RedissonClient redissonClient;

    @Transactional(rollbackFor = Exception.class)
    @Override
    public CommonResult<Boolean> generateCode(Integer couponId, Integer count) {
        if (count > 50) {
            return CommonResult.failed("每次生成兑换码不得超过50个");
        }
        // 查询优惠券信息
        ChargeCoupon record = chargeCouponMapper.selectByPrimaryKey(couponId);
        if (record == null) {
            return CommonResult.failed();
        }
        if (record.getStatus() != CouponStatusEnum.PUBLISHED.getStatus()) {
            return CommonResult.failed("优惠券状态不正确，不能生成兑换码！");
        }

        Date expire;
        // 计算优惠券过期时间
        if (record.getValidityType() == 1) {
            Integer expireType = record.getExpire();
            Integer expireUnit = record.getExpireUnit();

            LocalDate expireDate = ChargeCouponConditionExpireUnitEnum.calculateExpireDate(LocalDate.now(), expireUnit, expireType);
            if (expireDate == null) {
                log.info("优惠券id:{}，优惠券过期时间计算失败", couponId);
                return CommonResult.failed(ResultCode.PARAMETER_ERROR);
            }
            expire = Date.from(Timestamp.valueOf(expireDate.atTime(LocalTime.MIDNIGHT)).toInstant());
        } else {
            if (record.getValidityEndTime().before(new Date())) {
                log.info("优惠券id:{}，优惠券过期时间计算失败", couponId);
                return CommonResult.failed("优惠券已过期，不能再生成兑换编码！");
            }
            expire = record.getValidityEndTime();
        }
        //获取优惠券序列号
        long serialNum = redissonClient.getAtomicLong(ChargeMarketRedisKeyPrefix.EXCHANGE_CODE_COUNT_KEY).addAndGet(count);
        List<ExchangeCode> exchangeCodeList = new ArrayList<>();
        log.info("优惠券id:{}，生成兑换码数量:{}", couponId, count);
        //1w------1000--10
        //500
        //定时任务
        // 生成兑换码
        for (long i = serialNum - count + 1; i <= serialNum; i++) {
            ExchangeCode exchangeCode = new ExchangeCode();
            exchangeCode.setCode(CodeUtil.generateCode(i, couponId));
            exchangeCode.setCouponId(couponId);
            exchangeCode.setStatus(ExchangeCodeStatusEnum.NOT_USED.getCode());
            exchangeCode.setUpdateTime(new Date());
            exchangeCode.setExpiredTime(expire);
            exchangeCode.setCreateTime(new Date());
            exchangeCodeList.add(exchangeCode);
        }
        exchangeCodeMapper.insertBatch(exchangeCodeList);
        // 缓存兑换码信息到redis
        for (ExchangeCode exchangeCode : exchangeCodeList) {
            ExchangeCodeDto exchangeCodeDto = new ExchangeCodeDto();
            BeanUtils.copyProperties(exchangeCode, exchangeCodeDto);
            redissonClient.getBucket(RedisConstantPre.EXCHANGE_CODE_KEY + exchangeCode.getCode()).set(JSON.toJSONString(exchangeCodeDto));
        }
        return CommonResult.success(Boolean.TRUE);
    }

    @Override
    public CommonResult<BaseRespPageDto<List<ExchangeCodeResDto>>> page(ExchangeCodePageQueryReqDto exchangeCodePageQueryReqDto) {
        int pageNum = exchangeCodePageQueryReqDto.getPageNum() == null ? 1 : exchangeCodePageQueryReqDto.getPageNum();
        int pageSize = exchangeCodePageQueryReqDto.getPageSize() == null ? 10 : exchangeCodePageQueryReqDto.getPageSize();

        Page<ExchangeCode> page = PageHelper.startPage(pageNum, pageSize);
        List<ExchangeCodeResDto> exchangeCodeList = exchangeCodeMapper.seleteByCondition(exchangeCodePageQueryReqDto);
        for (ExchangeCodeResDto exchangeCode : exchangeCodeList) {
            RBucket<Object> bucket = redissonClient.getBucket(RedisConstantPre.USER_INFO_ID_PRE + exchangeCode.getUserId());
            if (bucket.isExists()) {
                ChargeUserLoginResDto chargeUserLoginResDto = JSON.parseObject(bucket.get().toString(), ChargeUserLoginResDto.class);
                exchangeCode.setUserName(chargeUserLoginResDto.getNickName());
            }
        }
        BaseRespPageDto<List<ExchangeCodeResDto>> baseRespPageDto = new BaseRespPageDto<>();
        baseRespPageDto.setPageNum(pageNum);
        baseRespPageDto.setPageSize(pageSize);
        baseRespPageDto.setTotalCount(page.getTotal());
        baseRespPageDto.setData(exchangeCodeList);
        return CommonResult.success(baseRespPageDto);
    }

    @Override
    public CommonResult<String> preUse(ExchangeCodePreUseReqDto exchangeCodePreUseReqDto) {
        String code = exchangeCodePreUseReqDto.getCode();
        BigDecimal orderAmount = exchangeCodePreUseReqDto.getAmount();
        String key=RedisConstantPre.EXCHANGE_CODE_KEY + code;
        RBucket<String> exchangeCodeBucket = redissonClient.getBucket(key);
        if (!exchangeCodeBucket.isExists()) {
            return CommonResult.failed("兑换码不存在！");
        }
        ExchangeCode exchangeCode = JSON.parseObject(exchangeCodeBucket.get(), ExchangeCode.class);
        if (exchangeCode == null) {
            return CommonResult.failed("兑换码不存在！");
        }
        if (exchangeCode.getStatus() != ExchangeCodeStatusEnum.NOT_USED.getCode()) {
            return CommonResult.failed("兑换码已被使用！");
        }
        if (exchangeCode.getExpiredTime().before(new Date())) {
            return CommonResult.failed("兑换码已过期！");
        }
        RBucket<String> couponBucket = redissonClient.getBucket(RedisConstantPre.COUPON_PRE + exchangeCode.getCouponId());
        // 计算优惠券金额
        ChargeCoupon chargeCoupon = JSON.parseObject(couponBucket.get(), ChargeCoupon.class);
        if (chargeCoupon == null) {
            chargeCoupon = chargeCouponMapper.selectByPrimaryKey(exchangeCode.getCouponId());
            if (chargeCoupon == null) {
                log.info("优惠券id:{}，优惠券不存在！", exchangeCode.getCouponId());
                return CommonResult.failed("优惠券不存在！");
            }
        }
        CalculateDiscountAmountService calculateDiscountAmountService = CalculateDiscountAmountStrategy.getCalculateDiscountAmountService(chargeCoupon.getUseType());
        BigDecimal bigDecimal = calculateDiscountAmountService.calculateDiscountAmount(orderAmount, chargeCoupon.getFaceValue());
        return CommonResult.success(bigDecimal.toString());
    }
}
